home *** CD-ROM | disk | FTP | other *** search
- /*
- ** Apple Macintosh Developer Technical Support
- **
- ** Program: MacShell
- ** File: ctlhandler.c
- ** Written by: Eric Soldan
- **
- ** Copyright © 1991 Apple Computer, Inc.
- ** All rights reserved.
- */
-
- /* This code implements the new 7.0 human-interface standards for both
- ** TextEdit and List controls. These standards include the following features:
- **
- ** 1) Tabbing between TextEdit and List controls within a window.
- ** 2) Displaying what item is active. The active TextEdit item is indicated
- ** by either a blinking caret, or a selection range.
- ** 3) List positioning via the keyboard. Entries on the keyboard automatically
- ** select and display the closest List item. Also, the up and down arrows
- ** scroll through the list.
- ** 4) Window scrollbars are handled.
- */
-
-
-
- /*****************************************************************************/
-
-
-
- #include "MacShell.h" /* Get the MacShell includes/typedefs, etc. */
- #include "MacShellCommon.h" /* Get the stuff in common with rez. */
- #include "MacShell.protos" /* Get the prototypes for MacShell. */
-
- #ifndef __CONTROLS__
- #include <Controls.h>
- #endif
-
- #ifndef __CTLHANDLER__
- #include "CtlHandler.h"
- #endif
-
- #ifndef __EVENTS__
- #include <Events.h>
- #endif
-
- #ifndef __LISTCONTROL__
- #include "ListControl.h"
- #endif
-
- #ifndef __OSUTILS__
- #include <OSUtils.h>
- #endif
-
- #ifndef __PACKAGES__
- #include <Packages.h>
- #endif
-
- #ifndef __TEXTEDITCONTROL__
- #include "TextEditControl.h"
- #endif
-
-
-
- /*****************************************************************************/
-
-
-
- static FileRecHndl gFrHndl;
- static Rect gScrollRct;
- static Boolean gVert;
-
- static pascal void ScrollActionProc(ControlHandle scrollCtl, short part);
-
-
-
- /*****************************************************************************/
-
-
-
- static Handle buttonProcs[3] = {nil, nil, nil};
-
-
-
- /*****************************************************************************/
- /*****************************************************************************/
- /*****************************************************************************/
-
-
-
- /* This function converts a control handle to a control number. The function
- ** simply walks the window's control list and counts how many controls it
- ** has to traverse before finding the target control. The smallest control
- ** number that can be returned is 1. This makes control numbers similar to
- ** dialog item numbers. */
-
- #pragma segment Controls
- short Ctl2CNum(ControlHandle ctl)
- {
- ControlHandle nextCtl;
- short ctlNum;
-
- nextCtl = ((WindowPeek)(*ctl)->contrlOwner)->controlList;
- for (ctlNum = 0;;) {
- if (!nextCtl) break;
- ++ctlNum;
- if (ctl == nextCtl) break;
- nextCtl = (*nextCtl)->nextControl;
- }
- return(ctlNum);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* This function reactivates the last active TextEdit or List control for
- ** the indicated window. */
-
- #pragma segment Controls
- void DoCtlActivate(WindowPtr window)
- {
- BeginContent(window);
-
- if (CLFindActive(window)) /* If the last item active was, */
- CLWindActivate(window); /* a List, then activate it. */
- else
- CTEWindActivate(window); /* Otherwise, activate last TextEdit control. */
-
- EndContent(window);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* This function returns which kind of button the control is. This does more
- ** than GetCVariant in that it makes sure that the control is actually a
- ** button. It does this by comparing the defProc against the known defProc
- ** for the various button types. For 7.0, there is only one defProc for all
- ** variants, but for pre-7.0, there is one defProc value for each variant.
- ** The method below handles either case. */
-
- #pragma segment Controls
- short GetButtonVariant(ControlHandle ctl)
- {
- short i;
- Rect dummy;
- ControlHandle dummyCtl;
-
- for (i = pushButProc; i <= radioButProc; ++i) {
- if (!buttonProcs[i]) {
- SetRect(&dummy, 0, 0, 0, 0);
- dummyCtl = NewControl((*ctl)->contrlOwner, &dummy, (ConstStr255Param)"\p", 1, false, 0, 1, i, 0L);
- if (dummyCtl) {
- buttonProcs[i] = (*dummyCtl)->contrlDefProc;
- DisposeControl(dummyCtl);
- }
- }
- if ((*ctl)->contrlDefProc == buttonProcs[i]) return(GetCVariant(ctl));
- }
-
- return(-1);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* This function returns all the checkBox values into the dsignated array.
- ** The function walks the control list, and when it finds a checkBox control,
- ** it gets the control value and places it in the next position in the array.
- ** This allows a single call to retrieve all the checkBox values at once. */
-
- #pragma segment Controls
- void GetCheckBoxValues(WindowPtr window, Boolean checkBoxVal[])
- {
- ControlHandle nextCtl;
- short checkBoxIndx;
-
- nextCtl = ((WindowPeek)window)->controlList;
- for (checkBoxIndx = 0;;) {
- if (!nextCtl) return;
- if (GetButtonVariant(nextCtl) == checkBoxProc)
- checkBoxVal[checkBoxIndx++] = GetCtlValue(nextCtl);
- nextCtl = (*nextCtl)->nextControl;
- }
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* This function returns which radio button is selected for a particular
- ** family of radio buttons. It finds the radio button of the target family
- ** with the lowest control number and it subtracts this number from the
- ** selected radio button of the same family. This gives a relative radio
- ** button number as a return result. This way the position of the family
- ** of radio buttons can change in the window and the return result is the
- ** same. */
-
- #pragma segment Controls
- short GetRadioButtonChoice(WindowPtr window, short famNum)
- {
- ControlHandle nextCtl;
- short ctlNum, firstInFam;
-
- nextCtl = ((WindowPeek)window)->controlList;
- for (ctlNum = 0, firstInFam = -1;;) {
- if (!nextCtl) return(-1); /* If a proper radio button family was
- ** passed in, this can't happen. The
- ** -1 is an error that indicates that
- ** the requested family didn't exist,
- ** or that there was no selected
- ** radio of the requested family. */
- ++ctlNum;
- if (GetButtonVariant(nextCtl) == radioButProc) {
- if (GetCRefCon(nextCtl) == famNum) {
- if (firstInFam == -1) firstInFam = ctlNum;
- if (GetCtlValue(nextCtl)) return(ctlNum - firstInFam);
- }
- }
- nextCtl = (*nextCtl)->nextControl;
- }
- }
-
-
-
- /*****************************************************************************/
-
-
-
- /* This function currently handles events for TextEdit, List, and button
- ** controls in a window. It also handles the window scrollbars and
- ** scrolling of the window. */
-
- #pragma segment Controls
- short IsCtlEvent(WindowPtr window, EventRecord *event,
- ControlHandle *retCtl, short *action)
- {
- RgnHandle frameRgn;
- TEHandle te, teNext;
- ListHandle list, listNext;
- ControlHandle ctl, activeCtl, teCtl, listCtl, nextCtl;
- short what, part, ctlNum, key, modifiers;
- Boolean hitFrame, hitScrollBar;
- Point clickLoc;
- CTEDataHndl teData;
-
- *retCtl = nil;
- *action = 0;
-
- if ((what = event->what) == mouseDown) {
-
- frameRgn = DoCalcFrameRgn(window);
- hitFrame = PtInRgn(event->where, frameRgn);
- DisposeRgn(frameRgn);
- if (hitFrame) return(HandleScrollEvent(window, event, retCtl, action));
- /* If window scrollbar is clicked on, handle the window scroll event. */
-
- BeginContent(window);
- clickLoc = event->where;
- GlobalToLocal(&clickLoc);
-
- CTECtlHit(); /* Clear CTECtl defProc's last hit CTECtl. */
- CLCtlHit(); /* Clear CLCtl defProc's last hit CLCtl. */
-
- if (part = FindControl(clickLoc, window, &ctl)) {
- hitScrollBar = IsScrollBar(ctl);
- /* The List controls and TextEdit controls may have scrollbars.
- ** Find out if a scrollbar was pressed, because it may belong
- ** to a TextEdit or List control. */
-
- ctlNum = Ctl2CNum(ctl);
-
- if ((hitScrollBar) || (CTECtlHit())) {
- /* This test is for speed. CTEClick would find out if a TextEdit
- ** control handled the mouse click, but not as fast as we would
- ** like. The above test determines if it is worth investigating. */
-
- if (CTEClick(event)) { /* If click belongs to a TextEdit control... */
- if (list = CLFindActive(window)) CLActivate(false, list);
- /* If old active control was a List control, deactivate it.
- ** The TextEdit control doesn't know how to do this. */
- *retCtl = ctl;
- /* Return the control of the TextEdit control. */
- EndContent(window);
- return(ctlNum);
- }
- }
-
- if ((hitScrollBar) || (CLCtlHit())) {
- /* This test is for speed. CLClick would find out if a List
- ** control handled the mouse click, but not as fast as we would
- ** like. The above test determines if it is worth investigating. */
-
- if (CLClick(event, action)) { /* If click belongs to a List control... */
- if (te = CTEFindActive(window)) CTEActivate(false, te);
- /* If old active control was a TextEdit control, deactivate it.
- ** The List control doesn't know how to do this. */
- *retCtl = ctl;
- /* Return the control of the TextEdit control. */
- EndContent(window);
- return(ctlNum);
- }
- }
-
- *action = 0;
-
- if (TrackControl(ctl, clickLoc, nil)) { /* Handle button controls. */
-
- switch(GetButtonVariant(ctl)) {
- case pushButProc:
- break;
- case checkBoxProc:
- SetCtlValue(ctl, GetCtlValue(ctl) ^ 1); /* Toggle checkBox value. */
- break;
- case radioButProc:
- nextCtl = ((WindowPeek)window)->controlList;
- /* The below loop walks the control list for the window and
- ** finds radio buttons of the correct family number. If
- ** the found radio button is the one that was clicked on,
- ** the value is set true, otherwise it is set false. */
- for (; nextCtl; nextCtl = (*nextCtl)->nextControl) {
- if (GetButtonVariant(nextCtl) == radioButProc)
- if (GetCRefCon(nextCtl) == GetCRefCon(ctl))
- SetCtlValue(nextCtl, (nextCtl == ctl));
- }
- break;
- }
- }
- *retCtl = ctl;
- EndContent(window);
- return(ctlNum);
- }
-
- EndContent(window);
- return(0);
- }
-
- if ((what == keyDown) || (what == autoKey)) { /* If event was keypress... */
-
- modifiers = event->modifiers;
- if (modifiers & cmdKey) return(0);
- /* Not our job to handle this one. */
-
- key = event->message & charCodeMask;
-
- if (key == 9) { /* If tab... */
-
- teNext = nil;
- listNext = nil;
- activeCtl = nil;
-
- if (te = CTEFindActive(window)) activeCtl = CTEViewFromTE(te);
- if (list = CLFindActive(window)) activeCtl = CLViewFromList(list);
- /* Find what the active control is. */
-
- if (!(teCtl = CTENext(window, &teNext, activeCtl)))
- teCtl = CTENext(window, &teNext, nil);
- /* Find the next TextEdit control from the active control. */
-
- if (!(listCtl = CLNext(window, &listNext, activeCtl)))
- listCtl = CLNext(window, &listNext, nil);
- /* Find the next List control from the active control. */
-
- if ((!teNext) && (!listNext)) return(0);
- /* No TextEdit or List controls in window, so we are done. */
-
- if (!activeCtl) nextCtl = ((WindowPeek)window)->controlList;
- else nextCtl = (*activeCtl)->nextControl;
- /* At this point we probably have the following information:
- ** 1) Active control.
- ** 2) First TextEdit control after the active control.
- ** 3) First List control after the active control.
- ** What we want to do is determine if the TextEdit control or
- ** the List control is closest to the active control.
- ** We may not have a currently active control, so in that case
- ** we will start at the beginning of the window control list. */
-
- for (;;) {
- if (!nextCtl) nextCtl = ((WindowPeek)window)->controlList;
-
- if (nextCtl == teCtl) { /* Activate a TextEdit control... */
- BeginContent(window); /* Clip out window scrollbars and growIcon. */
- if (list) CLActivate(false, list);
- CTEActivate(true, teNext);
- teData = (CTEDataHndl)(*teCtl)->contrlData;
- if ((*teData)->mode & cteTabSelectAll)
- CTESetSelect(0, (*teNext)->teLength, teNext);
- /* If the "select all TextEdit text when tabbed into" bit is
- ** set, then do that very thing. */
- EndContent(window); /* Remove the clipping. */
- return(Ctl2CNum(*retCtl = teCtl));
- }
-
- if (nextCtl == listCtl) {
- BeginContent(window); /* Clip out window scrollbars and growIcon. */
- if (te) CTEActivate(false, te);
- CLActivate(true, listNext);
- EndContent(window); /* Remove the clipping. */
- return(Ctl2CNum(*retCtl = listCtl));
- }
- nextCtl = (*nextCtl)->nextControl;
- }
- }
-
- if (te = CTEFindActive(window)) { /* If TextEdit control is active... */
- BeginContent(window);
- CTEKey(event); /* Allow key to be processed by the TextEdit control. */
- *action = 1;
- EndContent(window);
- return(Ctl2CNum(*retCtl = CTEViewFromTE(te)));
- }
-
- if (list = CLFindActive(window)) { /* If List control is active... */
- BeginContent(window);
- CLKey(event);
- EndContent(window);
- return(Ctl2CNum(*retCtl = CLViewFromList(list)));
- }
- }
-
- return(0);
- }
-
-
-
- /*****************************************************************************/
-
-
-
- #pragma segment Controls
- short HandleScrollEvent(WindowPtr window, EventRecord *event,
- ControlHandle *retCtl, short *action)
- {
- WindowPtr oldPort;
- Point clickLoc;
- short part;
- ControlHandle ctl;
- short value, h, v;
- RgnHandle updateRgn;
-
- GetPort(&oldPort);
- SetPort(window);
- gScrollRct = window->portRect;
-
- SetOrigin(0, -16384);
- clickLoc = event->where;
- GlobalToLocal(&clickLoc);
- /* Scrollbars for window are offset -16384 vertically. Get a local
- ** coordinate that corresponds to this negative space. */
-
- if (!(part = FindControl(clickLoc, window, &ctl))) {
- SetOrigin(gScrollRct.left, gScrollRct.top); /* Restore the origin. */
- *retCtl = nil;
- *action = 0;
- SetPort(oldPort);
- return(kScrollEvent);
- }
-
- gFrHndl = (FileRecHndl)GetWRefCon(window);
- if ((*gFrHndl)->fileState.hScroll) gScrollRct.right -= 15;
- if ((*gFrHndl)->fileState.vScroll) gScrollRct.bottom -= 15;
-
- gVert = (((*ctl)->contrlRect.right - (*ctl)->contrlRect.left) == 16);
- switch (part) {
- case inThumb:
- value = GetCtlValue(ctl);
- SetOrigin(0, -16384);
- part = TrackControl(ctl, clickLoc, nil);
- SetOrigin(gScrollRct.left, gScrollRct.top); /* Restore the origin. */
- if (part) {
- value -= GetCtlValue(ctl);
- /* Value now has CHANGE in position. if position changed, scroll. */
- if (value) {
- h = v = 0;
- if (gVert) v = value;
- else h = value;
- ScrollRect(&gScrollRct, h, v, updateRgn = NewRgn());
- InvalRgn(updateRgn);
- DisposeRgn(updateRgn);
- }
- }
- break;
- default:
- SetOrigin(0, -16384);
- TrackControl(ctl, clickLoc, (ProcPtr)ScrollActionProc);
- SetOrigin(gScrollRct.left, gScrollRct.top); /* Restore the origin. */
- break;
- }
-
- AdjustScrollBars(window);
-
- SetPort(oldPort);
- return(kScrollEvent);
- }
-
-
-
- /*****************************************************************************/
- /*****************************************************************************/
-
-
-
- #pragma segment Controls
- pascal void ScrollActionProc(ControlHandle scrollCtl, short part)
- {
- WindowPtr window;
- short delta, value, h, v;
- short oldValue, max;
- Point org;
- RgnHandle updateRgn, contPart, framePart;
-
- GetPort(&window);
-
- if (part) { /* If it was actually in the control. */
-
- switch (part) {
- case inUpButton:
- case inDownButton: /* One line. */
- delta = (gVert) ? (*gFrHndl)->fileState.vArrowVal : (*gFrHndl)->fileState.hArrowVal;
- break;
- case inPageUp: /* One page. */
- case inPageDown:
- delta = (gVert) ? (*gFrHndl)->fileState.vPageVal : (*gFrHndl)->fileState.hPageVal;
- break;
- }
-
- if ( (part == inUpButton) || (part == inPageUp) )
- delta = -delta; /* Reverse direction for an upper. */
-
- value = (oldValue = GetCtlValue(scrollCtl)) + delta;
- if (value < 0) value = 0;
- if (value > (max = GetCtlMax(scrollCtl))) value = max;
-
- if (value != oldValue) {
-
- SetCtlValue(scrollCtl, value);
- SetOrigin(gScrollRct.left, gScrollRct.top);
- h = oldValue - value;
- v = 0;
- if (gVert) {
- v = h;
- h = 0;
- }
-
- ScrollRect(&gScrollRct, h, v, updateRgn = NewRgn());
- InvalRgn(updateRgn);
- DisposeRgn(updateRgn);
-
- DoUpdateSeparate(window, &contPart, &framePart);
- if (framePart) DisposeRgn(framePart);
- if (contPart) {
- CopyRgn(contPart, ((WindowPeek)window)->updateRgn);
- DisposeRgn(contPart);
- BeginUpdate(window);
- GetContentOrigin(window, &org);
- SetOrigin(org.h, org.v);
- DoImageDocument(gFrHndl);
- EndUpdate(window);
- }
-
- SetOrigin(0, -16384);
- }
- }
- }
-
-
-
-